package chat

import (
	"fmt"

	"github.com/aliyun/aliyun-tablestore-go-sdk/tablestore"
	"github.com/pkg/errors"
)

var (
	// ErrTableStoreMessageContentNotFound 消息未找到
	ErrTableStoreMessageContentNotFound = errors.New("tableStore chat message content not found")
)

// TableStoreMessageContent 私聊消息内容
type TableStoreMessageContent struct {
	tableName   string
	client      *tablestore.TableStoreClient
	primaryKeys map[string]tablestore.PrimaryKeyType
	columns     map[string]tablestore.DefinedColumnType
	maxVersion  int
}

// NewTableStoreMessageContent 新建消息存储
func NewTableStoreMessageContent() *TableStoreMessageContent {
	return &TableStoreMessageContent{
		tableName: "messages",
		client:    tablestore.NewClient("https://demo.cn-qingdao.ots-internal.aliyuncs.com", "demo", "LTAI4G54tYWeBA1gsuLRPsMy", "FRMsLO1cAfVr70d0tf4mIAKewjG2jA"),
		primaryKeys: map[string]tablestore.PrimaryKeyType{
			"id": tablestore.PrimaryKeyType_STRING,
		},
		columns: map[string]tablestore.DefinedColumnType{
			"content": tablestore.DefinedColumn_STRING,
		},
		maxVersion: 1,
	}
}

// Create 创建表
func (ts *TableStoreMessageContent) Create() error {
	tableMeta := &tablestore.TableMeta{TableName: ts.tableName}
	for k, v := range ts.primaryKeys {
		tableMeta.AddPrimaryKeyColumn(k, v)
	}

	for k, v := range ts.columns {
		tableMeta.AddDefinedColumn(k, v)
	}

	_, err := ts.client.CreateTable(&tablestore.CreateTableRequest{
		TableMeta: tableMeta,
		TableOption: &tablestore.TableOption{
			TimeToAlive:               -1,
			DeviationCellVersionInSec: 0,
			MaxVersion:                ts.maxVersion,
		},
		ReservedThroughput: &tablestore.ReservedThroughput{},
	})

	return errors.Wrapf(err, "create table %s", ts.tableName)
}

// getPrimaryKey 获取主键
func (ts *TableStoreMessageContent) getPrimaryKey(id string) *tablestore.PrimaryKey {
	pk := &tablestore.PrimaryKey{}
	pk.AddPrimaryKeyColumn("id", id)
	return pk
}

// Get 单条获取
func (ts *TableStoreMessageContent) Get(id string) (string, error) {
	req := &tablestore.GetRowRequest{SingleRowQueryCriteria: &tablestore.SingleRowQueryCriteria{
		TableName:  ts.tableName,
		PrimaryKey: ts.getPrimaryKey(id),
		MaxVersion: int32(ts.maxVersion),
	}}

	res, err := ts.client.GetRow(req)
	if err != nil {
		return "", errors.Wrapf(err, "Get(%s) GetRow", id)
	}

	if len(res.Columns) == 0 {
		return "", ErrTableStoreMessageContentNotFound
	}

	return fmt.Sprintf("%s", res.Columns[0].Value), nil
}

// MGet 批量获取
func (ts *TableStoreMessageContent) MGet(ids []string) (map[string]string, error) {
	req := &tablestore.BatchGetRowRequest{}
	criteria := &tablestore.MultiRowQueryCriteria{TableName: ts.tableName}

	for _, id := range ids {
		criteria.AddRow(ts.getPrimaryKey(id))
		criteria.MaxVersion = ts.maxVersion
	}

	req.MultiRowQueryCriteria = append(req.MultiRowQueryCriteria, criteria)
	res, err := ts.client.BatchGetRow(req)
	if err != nil {
		return nil, errors.Wrapf(err, "HGet(%v) BatchGetRow()", ids)
	}

	values := make(map[string]string)
	for _, result := range res.TableToRowsResult {
		for _, v := range result {
			var id, value string
			if len(v.PrimaryKey.PrimaryKeys) > 0 {
				id = fmt.Sprintf("%s", v.PrimaryKey.PrimaryKeys[0].Value)
			}

			if len(v.Columns) > 0 {
				value = fmt.Sprintf("%s", v.Columns[0].Value)
			}

			if id != "" {
				values[id] = value
			}
		}
	}

	return values, nil
}

// Set 单条存储
func (ts *TableStoreMessageContent) Set(id, value string) error {
	req := &tablestore.PutRowRequest{
		PutRowChange: &tablestore.PutRowChange{
			TableName:  ts.tableName,
			PrimaryKey: ts.getPrimaryKey(id),
			Condition: &tablestore.RowCondition{
				RowExistenceExpectation: tablestore.RowExistenceExpectation_IGNORE,
			},
		},
	}
	req.PutRowChange.AddColumn("content", value)
	_, err := ts.client.PutRow(req)
	return errors.Wrapf(err, "Set(%s, %s) PutRow()", id, value)
}

// MSet 批量存储
func (ts *TableStoreMessageContent) MSet(values map[string]string) error {
	req := &tablestore.BatchWriteRowRequest{}
	for id, value := range values {
		pk := ts.getPrimaryKey(id)
		change := &tablestore.PutRowChange{TableName: ts.tableName, PrimaryKey: pk}
		change.AddColumn("content", value)
		change.SetCondition(tablestore.RowExistenceExpectation_IGNORE)
		req.AddRowChange(change)
	}
	_, err := ts.client.BatchWriteRow(req)
	return errors.Wrapf(err, "HSet(%v) BatchWriteRow()", values)
}

// Range 遍历数据
func (ts *TableStoreMessageContent) Range(handle func(id, value string) error) error {
	start := &tablestore.PrimaryKey{}
	start.AddPrimaryKeyColumnWithMinValue("id")
	end := &tablestore.PrimaryKey{}
	end.AddPrimaryKeyColumnWithMaxValue("id")

	GetRange := func(start, end *tablestore.PrimaryKey) (*tablestore.PrimaryKey, error) {
		req := &tablestore.GetRangeRequest{RangeRowQueryCriteria: &tablestore.RangeRowQueryCriteria{
			TableName:       ts.tableName,
			StartPrimaryKey: start,
			EndPrimaryKey:   end,
			MaxVersion:      1,
			Direction:       tablestore.FORWARD,
			Limit:           100,
		}}

		res, err := ts.client.GetRange(req)
		if err != nil {
			return nil, errors.Wrapf(err, "Range GetRange()")
		}

		for _, v := range res.Rows {
			var id, value string
			if len(v.PrimaryKey.PrimaryKeys) > 0 {
				id = fmt.Sprintf("%s", v.PrimaryKey.PrimaryKeys[0].Value)
			}

			if len(v.Columns) > 0 {
				value = fmt.Sprintf("%s", v.Columns[0].Value)
			}

			if id != "" {
				if err := handle(id, value); err != nil {
					return nil, errors.Wrap(err, "range handle")
				}
			}
		}

		return res.NextStartPrimaryKey, nil
	}

	for {
		next, err := GetRange(start, end)
		if err != nil {
			return errors.Wrap(err, "GetRange")
		}
		if next == nil {
			break
		}
		start = next
	}

	return nil
}
